#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "file.h"
#include <QDebug>
#include <QFile>
#include <QInputDialog>
#include <QMessageBox>

QString globaltext = "";
QString situation = "";

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->input->setPlaceholderText("请输入文件名 ");
    ui->show->setPlaceholderText("此处将展示当前执行文件的所有记录~ ");
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 人类
Personal::Personal() : nameLen(10), cityLen(10) {
    name = new char[nameLen + 1];
    name[nameLen] = '\0';  // end
    city = new char[cityLen + 1];
    city[cityLen] = '\0';  // end
}
Personal:: Personal(char* ssn, char* n, char* c, int y, long s):
        nameLen(10), cityLen(10) {
    name = new char[nameLen + 1];
    city = new char[cityLen + 1];
    qstrcpy(SSN, ssn);
    qstrcpy(name, n);
    qstrcpy(city, c);
    year = y;
    salary = s;
}
Personal& Personal::operator=(const Personal& other) {
    if (this != &other) {
        // copy personal to T
        strncpy_s(SSN, other.SSN, 9);
        if (name) {
            delete[] name;
        }
        name = new char[nameLen+1];
        name[nameLen] = '\0';  // 终止字符串
        qstrncpy(name, other.name, nameLen-1);
        if (city) {
            delete[] city;
        }
        city = new char[cityLen+1];
        city[cityLen] = '\0';  // 终止字符串
        qstrncpy(city, other.city, cityLen-1);
        year = other.year;
        salary = other.salary;
    }
    return *this;
}
istream& Personal::readFromDialog(istream& in, QString userInput) {
    // end
    SSN[9] = name[nameLen] = city[cityLen] = '\0';
    // use QInputDialog get userinput
    QStringList parts = userInput.split('\n');
    strncpy_s(SSN, parts.value(0).toStdString().c_str(), 9);
    qstrncpy(name, parts.value(1).toStdString().c_str(), nameLen);
    qstrncpy(city, parts.value(2).toStdString().c_str(), cityLen);
    year = parts.value(3).toInt();
    salary = parts.value(4).toLong();
    return in;
}
void Personal::writeToFile(fstream& out) const {
    out.write(SSN, 9);
    out.write(name, nameLen);
    out.write(city, cityLen);
    // cout a person info
    qDebug() << "  SSN:" << SSN << "  name:" << name << "  nameLen:" << nameLen << "  city:" << city << "  cityLen:" << cityLen;
    // binary
    out.write(reinterpret_cast<const char*> (&year), sizeof(int));
    out.write(reinterpret_cast<const char*> (&salary), sizeof(long));
}
void Personal::readFromFile(fstream& in) {
    in.read(SSN, 9);
    in.read(name, nameLen);
    in.read(city, cityLen);
    in.read(reinterpret_cast<char*> (&year), sizeof(int));
    in.read(reinterpret_cast<char*> (&salary), sizeof(long));
}
void Personal::readKey(QString input) {
    qstrncpy(SSN, input.toStdString().c_str(), 9);  // 9 char is SSN
}
QTextStream& Personal::writeLegibly(QTextStream& out) {
    SSN[9] = name[nameLen] = city[cityLen] = '\0';
    qDebug() << "Personal's writeLegibly ";
    out << "SSN =" << SSN << ", name =" << name
        << ", city = " << city << ", year =" << year
        << ", salary =" << salary;
    QString record = QString("SSN = %1, name = %2, city = %3, year = %4, salary = %5")
        .arg(SSN)
        .arg(name)
        .arg(city)
        .arg(year)
        .arg(salary);
    globaltext += record;
    qDebug() << record;
    return out;
}


// Student
Student::Student() : majorLen(10) {
    Personal();
    major = new char[majorLen + 1];
}
Student::Student(char* ssn, char* n, char* c, int y, long s, char* m) :
        majorLen(11) {
    Personal(ssn, n, c, y, s);
    major = new char[majorLen + 1];
    qstrcpy(major, m);
}
void Student::writeToFile(fstream& out) const {
    Personal::writeToFile(out);
    out.write(major, majorLen);
}
void Student::readFromFile(fstream & in) {
    Personal::readFromFile(in);
    in.read(major, majorLen);
}
QTextStream& Student::writeLegibly(QTextStream& out) {
    SSN[9] = name[nameLen] = city[cityLen] = major[majorLen] = '\0';
    qDebug() << "Student的writeLegibly ";
    out << "SSN =" << SSN << ", name =" << name
        << ", city = " << city << ", year =" << year
        << ", salary =" << salary << ", major =" << major;
    QString record = QString("SSN = %1, name = %2, city = %3, year = %4, salary = %5, major = %6")
        .arg(SSN)
        .arg(name)
        .arg(city)
        .arg(year)
        .arg(salary)
        .arg(major);
    globaltext += record;
    qDebug() << record;
    return out;
}
Student& Student::operator=(const Student& other) {
    if (this != &other) {
        // Student to T
        strncpy_s(SSN, other.SSN, 9);
        if (name) {
            delete[] name;
        }
        name = new char[nameLen+1];
        name[nameLen] = '\0';  // end
        qstrncpy(name, other.name, nameLen-1);

        if (city) {
            delete[] city;
        }
        city = new char[cityLen+1];
        city[cityLen] = '\0';  // end
        qstrncpy(city, other.city, cityLen-1);

        year = other.year;
        salary = other.salary;

        if (major) {
            delete[] major;
        }
        major = new char[majorLen+1];
        major[majorLen] = '\0';  // end
        qstrncpy(major, other.major, majorLen-1);
    }
    return *this;
}
istream& Student::readFromDialog(istream& in, QString userInput) {
    SSN[9] = name[nameLen] = city[cityLen] = major[majorLen] = '\0';
    // use QInputDialog get userinput
    QStringList parts = userInput.split('\n');
    strncpy_s(SSN, parts.value(0).toStdString().c_str(), 9);
    qstrncpy(name, parts.value(1).toStdString().c_str(), nameLen);
    qstrncpy(city, parts.value(2).toStdString().c_str(), cityLen);
    year = parts.value(3).toInt();
    salary = parts.value(4).toLong();
    qstrncpy(major, parts.value(5).toStdString().c_str(), 9);
    return in;
}


// Database
template<class T>
Database<T>::Database() {
}
template<class T>
void Database<T>::add(T& d){   // d is Personal or Student obj
    database.open(fName.toStdString().c_str(), ios::in | ios::out | ios::binary);
    database.clear();
    database.seekp(0, ios::end);
    d.writeToFile(database);   // add this record to this file by database
    database.close();
}
template<class T>
bool Database<T>::find(const T& d) {
    T tmp;
    database.open(fName.toStdString().c_str(), ios::in | ios::binary);
    database.clear();
    while (!database.eof()) {
        tmp.readFromFile(database);
        if (tmp == d) { // overloaded ==
            database.close();
            return true;
        }
    }
    database.close();
    return false;
}
template<class T>
void Database<T>::modify(const T& d) {
    T tmp;
    database.open(fName.toStdString().c_str(), ios::in | ios::out | ios::binary);
    database.clear();
    situation = "no such SSN";
    while (!database.eof()) {
        tmp.readFromFile(database);
        if (tmp == d) { // overloaded ==
            situation = "success modified";
            // cin >> tmp; // overloaded >>
            database.seekp(-d.size(), ios::cur);
            // back to this record' begin position
            d.writeToFile(database);
            database.close();
            return;
        }
    }
    database.close();
    cout << "The record to be modified is not in the database\n";
}
template<class T>
QTextStream& Database<T>::print(QTextStream& out) {
    T tmp;
    database.open(fName.toStdString().c_str(), ios::in | ios::binary);
    // init
    globaltext = "";
    while (true) {
        // qDebug() << "Database的print ";
        tmp.readFromFile(database);
        if (database.eof())
            break;
        out << tmp << endl; // tmp是personal类型的，所以会到personal里面的 overloaded <<  -> writeLegibly
        globaltext += "\n";
    }
    database.close();
    return out;
}
template<class T>
void Database<T>::run(QString fName1, int i, QString input) {
    fName = fName1;
    database.open(fName.toStdString().c_str(), ios::in);
    if (database.fail())
        database.open(fName.toStdString().c_str(), ios::out);
    database.close();

    // input to Personal/Student rec
    T rec;

    ifstream in;
    Personal pr;
    pr.readFromDialog(in, input);
    rec = pr;
    qDebug() << typeid(rec).name();


    if (i == 1) {
        // cin >> rec; // overloaded >>
        add(rec);
    }
    else if (i == 2) {
        rec.readKey(input);
        situation = "";
        if (find(rec) == false){
            situation = "this record is not in the database";
            return;
        }
    }
    else if (i == 3) {
        QStringList parts = input.split('\n');
        rec.readKey(parts.value(0));  // value(0)对应社会号SSN，将其作为键key，去寻找有没有该条记录
        modify(rec);
    }
    else return;

    qDebug() << "下面打印此文件的所有记录 ";
    QTextStream out(stdout);
    out << *this;  // db的<<重载，会到db.print里，利用QTextStream，重载<< 来输入该文件下的所有记录
    // cout << *this; // overloaded <<

}

template<class T>
void Database<T>::run2(QString fName1, int i, QString input) {
    fName = fName1;
    database.open(fName.toStdString().c_str(), ios::in);
    if (database.fail())
        database.open(fName.toStdString().c_str(), ios::out);
    database.close();

    T rec;  // 泛型T在此处应具体设为Student类型
    ifstream in;
    Student sr;
    sr.readFromDialog(in, input);
    rec = sr;  // 重载=，将sr这个学生类对象赋值给泛型rec
    qDebug() << typeid(rec).name();

    if (i == 1) {
        // cin >> rec; // overloaded >>
        add(rec);
    }
    else if (i == 2) {
        rec.readKey(input);
        situation = "";
        if (find(rec) == false){
            situation = "this record is not in the database";
            return;
        }
    }
    else if (i == 3) {
        QStringList parts = input.split('\n');
        rec.readKey(parts.value(0));
        modify(rec);
    }
    else return;

    qDebug() << "下面打印此文件的所有记录 ";
    QTextStream out(stdout);
    out << *this;  // db <<， -> db.print ，by QTextStream， (Personal's) <<
    // cout << *this; // overloaded <<
}


// Personal类的增查改操作
void MainWindow::on_add_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {    // fName 为空的处理逻辑
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "add Personal info: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        // 对Personal类型的用户信息进行处理
        Database<Personal>().run(fName, 1, userInput);
        ui->show->setText(globaltext);   // 展示信息到界面上
    } else {
        qDebug() << "用户取消输入 ";
    }
}

void MainWindow::on_find_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {     // fName 为空的处理逻辑
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "input SSN: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        Database<Personal>().run(fName, 2, userInput);
        if (situation == "this record is not in the database"){
            QMessageBox::warning(this, "提示", "无此条infomation");
        }else{
            QMessageBox::information(this, "提示", "查询成功! ");
            ui->show->setText(globaltext);
        }
    } else {
        qDebug() << "用户取消输入 ";
    }
}

void MainWindow::on_modify_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "modify Personal info: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        Database<Personal>().run(fName, 3, userInput);
        if (situation == "no such SSN"){
            QMessageBox::critical(this, "警告", "无此条SSN，修改failed");
        }else{
            ui->show->setText(globaltext);
        }
    } else {
        qDebug() << "用户取消输入 ";
    }
}

void MainWindow::on_exit_clicked()
{
    close();
}

void MainWindow::on_help_clicked()
{
    QMessageBox msgBox;
    msgBox.setWindowTitle("tips");
    msgBox.setText("随机访问文件包含3个功能点  <br> 增加需要输5条info <br> 查找需要输入社会号SSN "
                   "<br> 修改需要输5条info <br> 若SSN不存在会提示修改失败 <br> 展示框会显示当前文件所有info ");
    msgBox.setIcon(QMessageBox::Information);
    msgBox.exec();
}

// Student类的增查改操作
void MainWindow::on_add_2_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {    // fName 为空的处理逻辑
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "add Student info: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        // 将 QString 转换为 Student 类型
        Database<Student>().run2(fName, 1, userInput);
        ui->show->setText(globaltext);
    } else {
        qDebug() << "用户取消输入 ";
    }
}

void MainWindow::on_find_2_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {     // fName 为空的处理逻辑
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "input SSN: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        // 执行主过程，需传入文件名，用户输入，以及此次的操作（2表示find查找）
        Database<Student>().run2(fName, 2, userInput);
        if (situation == "this record is not in the database"){
            QMessageBox::warning(this, "提示", "无此条infomation");
        }else{
            QMessageBox::information(this, "提示", "查询成功! ");
            ui->show->setText(globaltext);
        }
    } else {
        qDebug() << "用户取消输入 ";
    }
}

void MainWindow::on_modify_2_clicked()
{
    QString fName = ui->input->text();
    if (fName.isEmpty()) {    // fName 为空的处理逻辑
        QMessageBox::warning(this, "提示", "输入不能为空，请重新输入");
        return;
    }
    bool ok;
    QString userInput = QInputDialog::getMultiLineText(this, "Dialog ", "modify Personal info: ", "", &ok);
    if (ok && !userInput.isEmpty()) {
        qDebug() << "用户输入的多行文本： " << userInput;
        // 将 QString 转换为 Personal 类型
        Database<Student>().run2(fName, 3, userInput);
        if (situation == "no such SSN"){
            QMessageBox::critical(this, "警告", "无此条SSN，修改failed");
        }else{
            ui->show->setText(globaltext);
        }
    } else {
        qDebug() << "用户取消输入 ";
    }
}
